//
// Created by wangp on 25-5-7.
//

#include <iostream>
#include "wsdd.nsmap"
#include "soapwsddProxy.h"
#include "wsaapi.h"
#include "onvif_comm.h"

// onvif规定的组播地址
//#define SOAP_MCAST_ADDR ""
//char SOAP_MCAST_ADDR[] = "soap.udp://239.255.255.250:3702";
////#define SOAP_TO         "urn:schemas-xmlsoap-org:ws:2005:04:discovery"
//char SOAP_TO[] = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";
////#define SOAP_ACTION     "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe"
//char SOAP_ACTION[] = "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
////#define SOAP_ITEM       ""                                                      // 寻找的设备范围
//char SOAP_ITEM[] = "";
////#define SOAP_TYPES      "dn:NetworkVideoTransmitter"                            // 寻找的设备类型
//char SOAP_TYPES[] = "dn:NetworkVideoTransmitter";
//#define SOAP_SOCK_TIMEOUT    (10)                                               // socket超时时间（单秒秒）

void soap_perror(struct soap* soap, const char* str)
{
    if (nullptr == str)
    {
        SOAP_DBGERR("[soap] error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    }
    else
    {
        SOAP_DBGERR("[soap] %s error: %d, %s, %s\n", str, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    }
}

void* ONVIF_soap_malloc(struct soap* soap, unsigned int n)
{
    void* p = nullptr;

    if (n > 0)
    {
        p = soap_malloc(soap, n);
        SOAP_ASSERT(NULL != p);
        memset(p, 0x00, n);
    }
    return p;
}

struct soap* ONVIF_soap_new(int timeout)
{
    struct soap* soap = NULL; // soap环境变量

    SOAP_ASSERT(NULL != (soap = soap_new()));

    soap_set_namespaces(soap, namespaces); // 设置soap的namespaces
    soap->recv_timeout = timeout; // 设置超时（超过指定时间没有数据就退出）
    soap->send_timeout = timeout;
    soap->connect_timeout = timeout;

#if defined(__linux__) || defined(__linux)
    soap->socket_flags = MSG_NOSIGNAL;
#endif
    // 设置为UTF-8编码，否则叠加中文OSD会乱码
    soap_set_mode(soap, SOAP_C_UTFSTRING);

    return soap;
}

void ONVIF_soap_delete(struct soap* soap)
{
    soap_destroy(soap); // remove deserialized class instances (C++ only)
    soap_end(soap); // Clean up deserialized data and temporary data
    soap_done(soap); // Reset, close communications, and remove callbacks
    soap_free(soap); // Reset and deallocate the context created with soap_new
}

/******************************************
**函数：ONVIF_init_header
**功能：初始化soap描述消息头
**参数：
        [in] soap - soap环境变量
**返回：无
**备注：
    1). 在本函数内部通过ONVIF_soap_malloc分配的内存，将在ONVIF_soap_delete中被释放
************************************************/
void ONVIF_init_header(struct soap* soap)
{
    struct SOAP_ENV__Header* header = NULL;

    SOAP_ASSERT(NULL != soap);

    header = (struct SOAP_ENV__Header*)ONVIF_soap_malloc(soap, sizeof(struct SOAP_ENV__Header));
    soap_default_SOAP_ENV__Header(soap, header);
    header->wsa__MessageID = (char*)soap_wsa_rand_uuid(soap);
    header->wsa__To = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TO) + 1);
    header->wsa__Action = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ACTION) + 1);
    strcpy(header->wsa__To, SOAP_TO);
    strcpy(header->wsa__Action, SOAP_ACTION);
    soap->header = header;
}

/****************************************
**函数：ONVIF_init_ProbeType
**功能：初始化探测设备的范围和类型
**参数：
        [in]  soap  - soap环境变量
        [out] probe - 填充要探测的设备范围和类型
**返回：
        0表明探测到，非0表明未探测到
**备注：
    1). 在本函数内部通过ONVIF_soap_malloc分配的内存，将在ONVIF_soap_delete中被释放
**************************************************/
void ONVIF_init_ProbeType(struct soap* soap, struct wsdd__ProbeType* probe)
{
    struct wsdd__ScopesType* scope = NULL; // 用于描述查找哪类的Web服务

    SOAP_ASSERT(NULL != soap);
    SOAP_ASSERT(NULL != probe);

    scope = (struct wsdd__ScopesType*)ONVIF_soap_malloc(soap, sizeof(struct wsdd__ScopesType));
    soap_default_wsdd__ScopesType(soap, scope); // 设置寻找设备的范围
    scope->__item = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ITEM) + 1);
    strcpy(scope->__item, SOAP_ITEM);

    memset(probe, 0x00, sizeof(struct wsdd__ProbeType));
    soap_default_wsdd__ProbeType(soap, probe);
    probe->Scopes = scope;
    probe->Types = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TYPES) + 1); // 设置寻找设备的类型
    strcpy(probe->Types, SOAP_TYPES);
}


int main(int argc, char **argv)
{
#if 1
    int i;
    int result = 0;
    unsigned int count = 0; // 搜索到的设备个数
    //struct soap* soap = NULL; // soap环境变量
    struct wsddProxy wsddTest;
    struct wsdd__ProbeType req; // 用于发送Probe消息
    struct __wsdd__ProbeMatches rep; // 用于接收Probe应答
    struct wsdd__ProbeMatchType* probeMatch;

    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    ONVIF_init_header(soap); // 设置消息头描述
    ONVIF_init_ProbeType(soap, &req); // 设置寻找的设备的范围和类型
    //result = soap_send___wsdd__Probe(soap, SOAP_MCAST_ADDR, NULL, &req); // 向组播地址广播Probe消息
    while (SOAP_OK == result) // 开始循环接收设备发送过来的消息
    {
        memset(&rep, 0x00, sizeof(rep));
        //result = soap_recv___wsdd__ProbeMatches(soap, &rep);
        if (SOAP_OK == result)
        {
            if (soap->error)
            {
                soap_perror(soap, "ProbeMatches");
            }
            else
            {
                // 成功接收到设备的应答消息
                //dump__wsdd__ProbeMatches(&rep);

                if (NULL != rep.wsdd__ProbeMatches)
                {
                    count += rep.wsdd__ProbeMatches->__sizeProbeMatch;
                    for (i = 0; i < rep.wsdd__ProbeMatches->__sizeProbeMatch; i++)
                    {
                        probeMatch = rep.wsdd__ProbeMatches->ProbeMatch + i;
                        //if (NULL != cb)
                        //{
                        //    cb(probeMatch->XAddrs); // 使用设备服务地址执行函数回调
                        //}
                    }
                }
            }
        }
        else if (soap->error)
        {
            break;
        }
    }

    SOAP_DBGLOG("\ndetect end! It has detected %d devices!\n", count);

    if (NULL != soap)
    {
        ONVIF_soap_delete(soap);
    }
#elif 0
    int i;
    unsigned int count = 0;// 搜索到的设备个数
    //struct soap *soap = nullptr;// soap环境变量
    //struct wsdd__ProbeType      req;// 用于发送Probe消息
    struct __wsdd__ProbeMatches rep; // 用于接收Probe应答
    struct wsdd__ProbeMatchType *probeMatch;
    struct wsddProxy wsddTest;
    soap_set_namespaces(wsddTest.soap,namespaces);// 设置soap的namespaces
    wsddTest.soap->recv_timeout = SOAP_SOCK_TIMEOUT;
    wsddTest.soap->send_timeout = SOAP_SOCK_TIMEOUT;
    wsddTest.soap->connect_timeout = SOAP_SOCK_TIMEOUT;
    wsddTest.soap->socket_flags = MSG_NOSIGNAL;
    // 设置为UTF-8编码，否则叠加中文OSD会乱码
    soap_set_mode(wsddTest.soap, SOAP_C_UTFSTRING);
    //初始化头部消息
    auto * header = static_cast<struct SOAP_ENV__Header*>(soap_malloc(wsddTest.soap, sizeof(struct SOAP_ENV__Header)));
    soap_default_SOAP_ENV__Header(wsddTest.soap, header);
    header->wsa__MessageID = const_cast<char*>(soap_wsa_rand_uuid(wsddTest.soap));
    header->wsa__To        = SOAP_TO;
    header->wsa__Action    = SOAP_ACTION;
    wsddTest.soap->header = header;
    // 设置寻找的设备的范围和类型
    struct wsdd__ProbeType req;// 用于发送Probe消息

    // 用于描述查找哪类的Web服务
    auto *scope = (struct wsdd__ScopesType *)soap_malloc(wsddTest.soap, sizeof(struct wsdd__ScopesType));
    soap_default_wsdd__ScopesType(wsddTest.soap, scope);// 设置寻找设备的范围
    scope->__item = (char*)soap_malloc(wsddTest.soap, strlen(SOAP_ITEM) + 1);
    strcpy(scope->__item, SOAP_ITEM);

    memset(&req, 0x00, sizeof(struct wsdd__ProbeType));
    soap_default_wsdd__ProbeType(wsddTest.soap, &req);
    req.Scopes = scope;
    req.Types  = (char*)soap_malloc(wsddTest.soap, strlen(SOAP_TYPES) + 1);     // 设置寻找设备的类型
    strcpy(req.Types, SOAP_TYPES);
    while (true)
    {
        int result = wsddTest.send_Probe(SOAP_MCAST_ADDR, nullptr, &req);        // 向组播地址广播Probe消息
        while (SOAP_OK == result) // 开始循环接收设备发送过来的消息
        {
            memset(&rep, 0x00, sizeof(rep));
            result = wsddTest.recv_ProbeMatches(rep);
            if (SOAP_OK == result)
            {
                if (wsddTest.soap->error)
                {
                    std::cerr << wsddTest.soap->error << std::endl;
                }
                else
                {
                    std::cout << "ProbeMatches" << std::endl;
                    if (nullptr != rep.wsdd__ProbeMatches)
                    {
                        count += rep.wsdd__ProbeMatches->__sizeProbeMatch;
                        for (i = 0; i < rep.wsdd__ProbeMatches->__sizeProbeMatch; i++)
                        {
                            probeMatch = rep.wsdd__ProbeMatches->ProbeMatch + i;
                            std::cout << probeMatch->XAddrs << std::endl;
                        }
                    }
                }
            }
            else if (wsddTest.soap->error)
            {
                std::cerr << wsddTest.soap->error << std::endl;
                break;
            }
        }
    }
#endif
    return 0;
}